﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

namespace AirLib
{
    public class VideoListener
    {

        #region Fields
        private HttpListener _httpListener = new HttpListener();
        private string _currentfile = null;
        private int _port;

        /// <summary>
        /// Gets the port.
        /// </summary>
        public int Port
        {
            get { return _port; }
        }

        /// <summary>
        /// Gets or sets the Current File to be Streamed.
        /// </summary>
        /// <value>
        /// The currentfile.
        /// </value>
        public string CurrentFile
        {
            get { return _currentfile; }
            set { _currentfile = value; }
        }
        #endregion Fields

        /// <summary>
        /// Initializes a new instance of the <see cref="VideoListener"/> class.
        /// </summary>
        /// <param name="port">The port.</param>
        /// <param name="localPath">The local path.</param>
        public VideoListener(int port)
        {
            _port = port;
            _httpListener.Prefixes.Add("http://*:" + _port + "/");
            _httpListener.Start();
            IAsyncResult result = _httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), _httpListener);
            _httpListener.IgnoreWriteExceptions = true;            
        }



        /// <summary>
        /// Listener event callback
        /// </summary>
        /// <param name="result">The result.</param>
        private void ListenerCallback(IAsyncResult result)
        {
            HttpListener listener = (HttpListener)result.AsyncState;            

            // Call EndGetContext to complete the asynchronous operation.
            HttpListenerContext context = listener.EndGetContext(result);

            _httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), _httpListener);
            
            //Obtain the request object.
            HttpListenerRequest request = context.Request;

            // Obtain the response object.
            HttpListenerResponse response = context.Response;

            FileInfo fileInfo = new FileInfo(CurrentFile);

            if (!request.RawUrl.EndsWith(fileInfo.Name))
            {
                response.StatusCode = 404;
                response.Close();
                return;
            }

            //Figure out the range of bytes requested by the AppleTV
            int startRange;
            int endRange;
            if (request.Headers["Range"] != null)
            {
                string requestedRange = request.Headers["Range"].Replace("bytes=", string.Empty);
                startRange = int.Parse(requestedRange.Split('-').First());
                endRange = int.Parse(requestedRange.Split('-').Last());
            }
            else
            {
                startRange = 0;
                endRange = (int)fileInfo.Length;
            }
            
            

            // Construct a response.
            response.StatusCode = (int)HttpStatusCode.PartialContent;
            response.KeepAlive = true;            
            response.Headers[HttpResponseHeader.Server] = "AirLib/1.0";
            response.Headers[HttpResponseHeader.ContentRange] = "bytes " + startRange + "-" + endRange + "/" + fileInfo.Length;
            response.Headers[HttpResponseHeader.ContentType] = GuessContentType(fileInfo.Extension);
            response.Headers[HttpResponseHeader.CacheControl] = "no-cache";
            response.Headers[HttpResponseHeader.Connection] = "Close";               
            
            byte[] buffer = new byte[endRange - startRange + 1];
            response.ContentLength64 = buffer.Length;


            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                fileStream.Seek(startRange,0);
                fileStream.Read(buffer, 0, buffer.Length);                
                fileStream.Close();
            }

            System.IO.Stream output = response.OutputStream;
            try
            {
                //Note: AppleTV seems to like to terminate stream and restart using another connnection
                //Error here does not indicate that streaming process has failed.
                output.Write(buffer, 0, buffer.Length);
                output.Close();
            }
            catch (Exception)
            {
                response.Abort();
            }
            finally
            {
                response.Close();
            }
            
        }

        /// <summary>
        /// Guesses the type of the content based on the file extension.
        /// </summary>
        /// <param name="extension">The ext.</param>
        /// <returns></returns>
        static private string GuessContentType(string extension)
        {
            switch (extension)
            {
                case ".mov":
                    return "video/quicktime";
                case ".m4v":
                    return "video/x-m4v";
                case ".avi":
                    return "video/x-msvideo";
                case ".mpg":
                    return "video/mpeg";
                case ".dvr-ms":
                    return "video/x-msvideo";
                case ".mp4":
                    return "video/mp4";
                case ".wmv":
                    return "video/x-ms-wmv";
                default:
                    return "application/octet-stream";
            }
        }
    }
}
